package relu.R.landscape;
import java.util.*;

import jfm.utils.*;
import jfm.model.*;
import jfm.model.Types.CropType;
import relu.weeds.*;
import relu.R.weeds.TransitionMatrix;
import relu.R.weeds.WeedSpecies;
import jfm.mou.*;
import relu.R.birds.*;
import relu.R.weeds.*;


public class SpatialLandscape extends Landscape {
	protected double area=0;
	public double sideLength;
	private ArrayList<FarmArea> locations=new ArrayList<FarmArea>();
	private Stack<FarmArea> unAssignedLocations=new Stack<FarmArea>();
	private double[][] breedingMap=null;
//	private Map<BirdSpecies,Integer> numBirds=new HashMap<BirdSpecies,Integer>();
	
	
	public double[][] breedingMap(){
		return breedingMap;
	}
	
	public double[][] getBirdHomeRangeMap(String spString,int asImage){
		LSMap theMap=new LSMap();
		for(Bird bd:birds){
			if ( bd.species.name.equals(spString)){
				theMap.addPoint(bd.xHome, bd.yHome, bd.species.homeRange);
			}
		}
		if(asImage>0){
			return theMap.toImage(sideLength, 20);
		} else {
			return theMap.getPoints();
		}
	}
	
	public double[][] getDeadBirdsMap(String spString,int asImage){
		LSMap theMap=new LSMap();
		for(Bird bd:deadBirds){
			if ( bd.species.name.equals(spString)){
				theMap.addPoint(bd.xHome, bd.yHome, bd.species.homeRange);
			}
		}
		if(asImage>0){
			return theMap.toImage(sideLength, 20);
		} else {
			return theMap.getPoints();
		}
	}
	
	public double[][] getWeedAbundanceImage(int grid,String weedSp){

		double[][] foodMap=new double[grid][grid];
		WeedSpecies weedsp=stringToSpecies.get(weedSp);
		JFMMath.doubleZero(foodMap);
		double[] bins=new double[grid+1];
		double delta=sideLength/grid;
		bins[0]=0;
		for( int i=0;i<grid;i++){
			bins[i+1]=bins[i]+delta;
		}
		for(int i=0;i<grid;i++){
			for(int j=0;j<grid;j++){
				for(Field fld: fields){
					if ( fld.xPos>bins[i] && fld.xPos <= bins[i+1] 	
					&& fld.yPos>bins[j] && fld.yPos <=bins[j+1]){			
						foodMap[i][j]+=fld.getDensity(weedsp);
					}
				}
			}
		}		
		return foodMap;
	}
	
	public double[][] getTotalFoodMap(int grid,String birdSp){
		double[][] foodMap=new double[grid][grid];
		BirdSpecies species=null;
		for(BirdSpecies sp:birdSpecies){
			if ( sp.name.equals(birdSp)){
				species=sp;
				break;
			}
		}
		if ( species ==null){
			throw new Error("No Bird species "+birdSp);
		}
		JFMMath.doubleZero(foodMap);
		double[] bins=new double[grid+1];
		double delta=sideLength/grid;
		bins[0]=0;
		for( int i=0;i<grid;i++){
			bins[i+1]=bins[i]+delta;
		}
		for(int i=0;i<grid;i++){
			for(int j=0;j<grid;j++){
				for(Field fld: fields){
					if ( fld.xPos>bins[i] && fld.xPos <= bins[i+1] 	
					&& fld.yPos>bins[j] && fld.yPos <=bins[j+1]){			
						for(WeedSpecies weedsp:species.diet){
							foodMap[i][j]+=fld.getDensity(weedsp);
						}
					}
				}
			}
		}		
		return foodMap;
	}
	
	public double[][] getWeedAbundanceMap(String spString){
		WeedSpecies sp=SpatialLandscape.stringToSpecies.get(spString);
		LSMap theMap=new LSMap();
		for(Field fld:fields){
			theMap.addPoint(fld.xPos,fld.yPos,fld.getDensity(sp));
		}
		return theMap.getPoints();
	}

	public double[][] getLocationMap(){
		double[][] lmap=new double[3][locations.size()];
		for(int i=0;i<locations.size();i++){
			lmap[0][i]=locations.get(i).xPos;
			lmap[1][i]=locations.get(i).yPos;
			lmap[2][i]=locations.get(i).radius;
		}
		return lmap;
	}
	
	public double[][] getFarmMap(){
		double[][] fmap=new double[3][farms.size()];
		
		for(int i=0;i<farms.size();i++){
		//	fmap[0][i]=foldCoordinate(sideLength,farms.get(i).xPos);
		//	fmap[1][i]=foldCoordinate(sideLength,farms.get(i).yPos);
			fmap[0][i]=farms.get(i).location.xPos;
			fmap[1][i]=farms.get(i).location.yPos;
			fmap[2][i]=farms.get(i).location.radius;
		}
		return fmap;
	}
	
	
	
	public void assignFieldsToFarmers(){
		for(Field f:fields){
			f.assignToNearestFarm(this);
		}
	}
	public void assignFieldsToBirds(){
		for(Field f:fields){
			f.assignToAllBirdsInRange(birds);
		}
	}
	
	public static double farmFieldDistance(LSFarm fm,Field fld){
		double dx=fm.location.xPos-fld.xPos;
		double dy=fm.location.yPos-fld.yPos;
		return Math.sqrt(dx*dx+dy*dy);
	}
	
	/** Create a stack of identical farms all with a particular Multi-objective utility*/
	public void addFarm(double inArea,String tmplXML,String mouXML){
		FarmerMOU fmou = FarmerMOU.fromXML(mouXML);
		System.out.println("Adding farm "+tmplXML+" mouXML "+inArea);
		// Find a location for this farm
		Iterator<FarmArea> it=unAssignedLocations.iterator();
		FarmArea loc=null;
		while(it.hasNext()){
			FarmArea fa=it.next();
			if ( Math.abs(fa.LParea-inArea) < 1 ){
				loc=fa;
				unAssignedLocations.remove(fa);
				break;
			}
		}
		if ( loc==null){
			throw new Error("No remaining location with area "+inArea);
		}
		LSFarm farm=LSFarm.fromXML(tmplXML,this,loc,inArea);
		if ( inArea != farm.area){
			throw new Error("Area "+inArea+" supplied does not match "+farm.area+" in file "+tmplXML);
		}
		farm.applyMOUToFarm(fmou);
		farms.add(farm);
	}
	

	public void stepSummer(int nBirds){
		System.out.println("Stepping year");
		// Update weed populations on all the fields
		System.out.println("Rotating Crops and propagating weeds ");
		for(Field fld:fields){
			fld.rotateCropsAndPropagateWeeds();
		}
		System.out.println("Creating summer bird pops");
		for(BirdSpecies bsp:birdSpecies){
			createSummerBirdPopulations(nBirds,bsp);
		}
		System.out.println("assigning fields to birds");
		assignFieldsToBirds();
	}
	
	/** Add a stack of identical farms 
	public void addFarms(String tmplXML,int nFarms){
		for ( int f=0;f<nFarms;f++){
			System.out.println("Adding farm "+f);
			farms.add(LSFarm.fromXML(tmplXML,this,0,0));
		}
	}*/
	
	
	public void generateFarmSpatialDistribution(double[] areas,double packingFraction){
		double totalArea=JFMMath.sum(areas);
		area=totalArea;
		locations.clear();
		sideLength=Math.sqrt(totalArea/packingFraction);
		for(int i=0;i<areas.length;i++){
			double xrand=rGen.nextDouble()*sideLength;
			double yrand=rGen.nextDouble()*sideLength;
			locations.add(new FarmArea(this,xrand,yrand,areas[i]));
		}
		unAssignedLocations.addAll(locations);
	}
	public void relaxLocations(int timeSteps,double packingFraction){
		double slgoal=Math.sqrt(area/packingFraction);
		double deltasl=(slgoal-sideLength)/timeSteps;
//		System.out.println("relaxing with "+deltasl+" total a"+area+" "+sideLength);
		for(int i=0;i<timeSteps;i++){
			ClosePacker.relaxFarmPositions(locations);
			sideLength+=deltasl;
//			System.out.println(sideLength);
		}
//		area=sideLength*sideLength;
	}
	
	public LSFarm owningFarm(double x,double y){
		int nearest=-1;
		double closestD=100000000.0;// Really far away
		for(int i=0;i<farms.size();i++){
			double dist=(Math.abs(farms.get(i).distanceFrom(sideLength,x,y))-farms.get(i).location.radius);
			if ( dist < closestD){
				nearest=i;
				closestD=dist;
			}
		}
		return farms.get(nearest);
	}
	
	/** This needs to be called after we have set the areas for all the farms
	 * 
	 * All of the farms have different areas. We need to place them 
	 * in space so that they are not overlapping and so that they completely fill the 
	 * available space.
	public void spatiallyDistributeFarms(){
		// For testing we just do it randomly .. needs to be way more sophisticated.
		double totalFarmArea=0;
		for(LSFarm farm:farms){
			totalFarmArea+=farm.area;
		}
		area=totalFarmArea;
		sideLength=Math.sqrt(area/0.6);
		for(LSFarm farm:farms){
			double x=rGen.nextDouble()*sideLength;
			double y=rGen.nextDouble()*sideLength;
//			x=sideLength
			farm.setPosition(x, y);
		}
	//	ClosePacker.relaxFarmPositions(farms);
	}
	public void relaxFarms(int timeSteps){
		for(int i=0;i<timeSteps;i++){
			ClosePacker.relaxFarmPositions(farms);
		}
	}
	*/
	
	
	
	/** This assumes that farmers are already set up */
	public void createAndDistributeFields(int nFields){
		fields.clear();
		// First check that the area is properly set 
		if ( area == 0 ){throw new Error("Can't assign fields because area not yet set");}
		double sideLength=Math.sqrt(area);
		for(int i=0;i<nFields;i++){
			double x=rGen.nextDouble()*sideLength;
			double y=rGen.nextDouble()*sideLength;
			fields.add(new Field(x,y));
		}
		this.assignFieldsToFarmers();
	}
	
	
	

	
	public void createSummerBirdPopulations(int nBirds,BirdSpecies sp){
		int grid=20;
		double sideLength=Math.sqrt(area);
		breedingMap=BreedingHabitats.getBreedingProbabilityMap(this,grid);
		birds.clear();
		deadBirds.clear();
		for(int i=0;i<nBirds;i++){
			double[] birdPos=BreedingHabitats.samplePoint(breedingMap,sideLength,rGen.nextDouble());
			birds.add(new Bird(sp,birdPos[0],birdPos[1]));
		}
	}
	
	
	/*
	public void applyMOUToFarms(FarmerMOU mou){
		for(LSFarm farm:farms){
			farm.applyMOUToFarm(mou);
		}
	}
	
	public void applyYieldsForCrop(String cropName,double[] yields){
		try {
		Types.CropType cropType=Types.xmlToCropType(cropName);
		for(int f=0;f<yields.length;f++){
			System.out.println("Applying yield "+yields[f]);
			Farm farm=farms.get(f).farmLP;
			farm.cropping.getCrop(cropType).resetPrimaryYield(String.valueOf(yields[f]));
		}
		} catch(XMLSyntaxException ex){
			throw new Error(ex.getMessage());
		}
	}
	*/
	
}
